#include <arpa/inet.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <unistd.h>

static bool will_exit = false;

static void *
jserver_receive(void *data)
{
        int  sock = *((int *)data);
        char buffer[256];

        while (1) {
                memset(buffer, 0, 256);
                ssize_t state = recv(sock, buffer, 256, 0);
                if (state == -1) {
                        perror("recv");
                        will_exit = true;
                        return NULL;
                } else if (state == 0) {
                        printf("\n服务端已关闭连接。\n");
                        will_exit = true;
                        return NULL;
                }
                printf("接收到 %ld byte: %s\n", state, buffer);
        }
}

static size_t
fgets_b(char *__restrict__ __s)
{
        char *p;
        p = fgets(__s, 255, stdin);
        do {
                if (*p == '\n')
                        *p = '\0';
                else
                        p += 1;
        } while (*p);
        return p - __s;
}

static void
send_b(int __fd, const char *__buf)
{
        if (will_exit) {
                close(__fd);
                exit(0);
        }
        ssize_t lens = send(__fd, __buf, strlen(__buf), 0);
        if (lens == -1) {
                perror("send");
                printf("无法发送：连接已关闭。\n");
                close(__fd);
                exit(0);
        }
        return;
}

int
main(int argc, char *argv[])
{
	bool   is_admin_device = false;
        struct sockaddr addr;
        pthread_t       th;
        char            buffer[256], dev_type[10], dev_id[40], msg_head[100],
                target_id[40], pub_msg[50];

        if (argc != 2) {
                fprintf(stderr, "用法：jclient [JavaServer IP]\n");
                return 1;
        }

        int sock = socket(AF_INET, SOCK_STREAM, 0);
        if (sock == -1) {
                perror("socket");
                return 1;
        }

        ((struct sockaddr_in *)(&addr))->sin_port   = htons(2933);
        ((struct sockaddr_in *)(&addr))->sin_family = AF_INET;
        inet_pton(AF_INET, argv[1], &((struct sockaddr_in *)(&addr))->sin_addr);

        if (connect(sock, &addr, sizeof(addr)) == -1) {
                perror("connect");
                close(sock);
                return 2;
        }

        if (pthread_create(&th, NULL, jserver_receive, &sock)) {
                perror("pthread_create");
                close(sock);
                return 3;
        }
        pthread_detach(th);

retry:
        printf("输入设备类型[EMB,TERM]: ");
        fflush(stdout);
        fgets_b(dev_type);
        if (strcmp(dev_type, "EMB") &&
		strcmp(dev_type, "TERM") &&
		strcmp(dev_type, "ADMIN")) {
                goto retry;
	}

	if (!strcmp(dev_type, "ADMIN")) {
		is_admin_device = true;
	}

        printf("请指定设备 ID 号码: ");
        fflush(stdout);
        fgets_b(dev_id);

        printf("请设定要发布的消息名称，如“name,age,value”: ");
        fflush(stdout);
        fgets_b(msg_head);
        sprintf(buffer,
                "zncdz,devType=%s,msgType=REG,devID=%s,%s",
                dev_type,
                dev_id,
                msg_head);
        send_b(sock, buffer);

subcribe:
        printf("请输入订阅消息的设备 ID（输入 no 不订阅）: ");
        fflush(stdout);
        fgets_b(target_id);
        if (strcmp(target_id, "no") != 0) {
                sprintf(buffer,
                        "zncdz,devType=%s,msgType=SUB,devID=%s,tgtID=%s",
                        dev_type,
                        dev_id,
                        target_id);
                send_b(sock, buffer);
        }

        printf("可以发布消息了。\n");
        while (1) {
                printf("> ");
                fflush(stdout);
                fgets_b(pub_msg);
                if (will_exit) {
                        printf("退出程序。\n");
                        close(sock);
                        return 4;
                }
                if (strcmp(pub_msg, "quit") == 0) {
                        close(sock);
                        exit(0);
                }
                if (strcmp(pub_msg, "sub") == 0)
                        goto subcribe;
                sprintf(buffer,
                        "zncdz,devType=%s,msgType=%s,devID=%s,%s",
                        dev_type,
			(is_admin_device ? "CONTROL" : "PUB"),
                        dev_id,
                        pub_msg);
                send_b(sock, buffer);
        }

        return 0;
}
